package com.xinxin.blockchain.asset.template;

import com.xinxin.base.exception.BusinessException;
import com.xinxin.blockchain.asset.entity.AssetOperateParam;
import com.xinxin.blockchain.asset.enums.AssetBusinessTypeEnum;
import com.xinxin.blockchain.asset.exception.AssetShortageException;
import com.xinxin.blockchain.asset.strategy.AssetOperateStrategy;
import com.xinxin.blockchain.constant.ErrorCode;
import com.xinxin.blockchain.dao.IAccountAssetRecordDao;
import com.xinxin.blockchain.model.AccountAsset;
import com.xinxin.blockchain.model.AccountAssetRecord;
import com.xinxin.blockchain.service.IAccountAssetService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
 * @author ys
 * @date 2021/5/24
 */
public abstract class AbstractAssetTemplate {

    @Autowired
    private IAccountAssetRecordDao accountAssetRecordDao;

    @Autowired
    private IAccountAssetService accountAssetService;

    @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
    public AccountAsset operate(AssetOperateParam operateParam) {
        final Long accountId = operateParam.getAccountId();
        final Integer assetType = operateParam.getAssetType();
        final BigDecimal amount = operateParam.getAmount();
        final AssetBusinessTypeEnum businessType = operateParam.getBusinessType();

        final AccountAsset asset = accountAssetService.forceGetByType(accountId, assetType);

        return operate(asset, amount, businessType);
    }

    @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
    public AccountAsset operate(AccountAsset asset, BigDecimal amount, AssetBusinessTypeEnum businessType) {
        try {
            boolean result = getOperateStrategy().operate(asset, amount);
            if (!result) {
                throw new OptimisticLockingFailureException("资产更新失败");
            }
        } catch (AssetShortageException e) {
            throw new BusinessException(ErrorCode.ASSET_NOT_ENOUGH);
        }

        accountAssetRecordDao.save(record(asset, amount, businessType));

        return accountAssetService.getById(asset.getId());
    }

    protected abstract AssetOperateStrategy getOperateStrategy();

    protected abstract AccountAssetRecord record(AccountAsset asset, BigDecimal amount, AssetBusinessTypeEnum businessType);
}
